1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.audio; 12 13 public import hip.audio.audiosource; 14 public import hip.api.audio : HipAudioType, DistanceModel, HipAudioImplementation, getAudioImplementationForOS, IHipAudioPlayer, setIHipAudioPlayer; 15 import hip.config.audio; 16 17 //Backends 18 import hip.audio_decoding.audio; 19 import hip.math.utils:getClosestMultiple; 20 import hip.error.handler; 21 22 class HipAudioImpl : IHipAudioPlayer 23 { 24 public bool initialize(HipAudioImplementation implementation = HipAudioImplementation.OpenAL, 25 bool hasProAudio = false, 26 bool hasLowLatencyAudio = false, 27 int optimalBufferSize = 4096, 28 int optimalSampleRate = 44_100) 29 { 30 import hip.console.log; 31 ErrorHandler.startListeningForErrors("HipremeAudio initialization"); 32 _hasInitializedAudio = true; 33 this.is3D = is3D; 34 audioInterface = getAudioInterface(implementation); 35 this.hasProAudio = hasProAudio; 36 this.hasLowLatencyAudio = hasLowLatencyAudio; 37 this.optimalBufferSize = optimalBufferSize; 38 this.optimalSampleRate = optimalSampleRate; 39 40 loglnInfo("Hipreme Audio Started: ", implementation, 41 "\nPro Audio: ", hasProAudio, 42 "\nLow Latency: ", hasLowLatencyAudio, 43 "\nOptimal Buffer Size: ", optimalBufferSize, 44 "\nOptimal Sample Rate: ", optimalSampleRate 45 ); 46 return ErrorHandler.stopListeningForErrors(); 47 } 48 bool pause(AHipAudioSource src) 49 { 50 src.isPlaying = false; 51 return false; 52 } 53 bool play_streamed(AHipAudioSource src) 54 { 55 audioInterface.play_streamed(src); 56 src.isPlaying = true; 57 return false; 58 } 59 IHipAudioClip getClip(){return audioInterface.getClip();} 60 61 /** 62 * Loads a file from disk, sets the chunkSize for streaming and does one decoding frame 63 */ 64 IHipAudioClip loadStreamed(string path, uint chunkSize = ushort.max+1) 65 { 66 chunkSize = getClosestMultiple(optimalBufferSize, chunkSize); 67 IHipAudioClip buf = audioInterface.loadStreamed(path, chunkSize); 68 return buf; 69 } 70 71 void updateStream(AHipAudioSource source) 72 { 73 audioInterface.updateStream(source); 74 } 75 AHipAudioSource getSource(bool isStreamed = false, IHipAudioClip clip = null) 76 { 77 if(isStreamed) ErrorHandler.assertExit(clip !is null, "Can't get streamed source without any buffer"); 78 HipAudioSource ret = cast(HipAudioSource)audioInterface.getSource(isStreamed); 79 if(clip) 80 ret.clip = clip; 81 return ret; 82 } 83 void onDestroy() 84 { 85 if(audioInterface !is null) 86 audioInterface.onDestroy(); 87 audioInterface = null; 88 } 89 90 void update() 91 { 92 if(audioInterface !is null) 93 audioInterface.update(); 94 } 95 96 private static IHipAudioPlayer getAudioInterface(HipAudioImplementation impl, 97 bool hasProAudio = false, 98 bool hasLowLatencyAudio = false, 99 int optimalBufferSize = 4096, 100 int optimalSampleRate = 44_100) 101 { 102 import hip.console.log; 103 final switch(impl) 104 { 105 case HipAudioImplementation.WebAudio: 106 { 107 static if(HasWebAudio) 108 { 109 import hip.audio.backend.webaudio.player; 110 return new HipWebAudioPlayer(AudioConfig.musicConfig); 111 } 112 else 113 { 114 loglnWarn("Tried to use WebAudio implementation, but not in WebAssembly. No audio available"); 115 goto case HipAudioImplementation.Null; 116 } 117 } 118 case HipAudioImplementation.OpenSLES: 119 static if(HasOpenSLES) 120 { 121 import hip.audio.backend.opensles.player; 122 return new HipOpenSLESAudioPlayer(AudioConfig.androidConfig, 123 hasProAudio, 124 hasLowLatencyAudio, 125 optimalBufferSize, 126 optimalSampleRate); 127 break; 128 } 129 case HipAudioImplementation.XAudio2: 130 static if(HasXAudio2) 131 { 132 import hip.audio.backend.xaudio.player; 133 loglnInfo("Initializing XAudio2 with audio config ", AudioConfig.musicConfig); 134 return new HipXAudioPlayer(AudioConfig.musicConfig); 135 } 136 else 137 { 138 loglnWarn("Tried to use XAudio2 implementation, but no XAudio2 version was provided. OpenAL will be used instead"); 139 goto case HipAudioImplementation.OpenAL; 140 } 141 case HipAudioImplementation.AVAudioEngine: 142 { 143 static if(HasAVAudioEngine) 144 { 145 import hip.audio.backend.avaudio.player; 146 return new HipAVAudioPlayer(AudioConfig.androidConfig); 147 } 148 else 149 { 150 loglnWarn("Tried to use AVAudioEngine implementation, but no AVAudioEngine found. OpenAL will be used instead"); 151 goto case HipAudioImplementation.OpenAL; 152 } 153 } 154 case HipAudioImplementation.OpenAL: 155 { 156 static if(HasOpenAL) 157 { 158 import hip.audio.backend.openal.player; 159 //Please note that OpenAL HRTF(spatial sound) only works with Mono Channel 160 return new HipOpenALAudioPlayer(AudioConfig.musicConfig); 161 } 162 else 163 { 164 loglnWarn("Tried to use OpenAL implementation, but no OpenAL version was provided. No audio available."); 165 goto case HipAudioImplementation.Null; 166 } 167 } 168 case HipAudioImplementation.Null: 169 { 170 import hip.audio.backend.nullaudio; 171 loglnWarn("No AudioInterface was found. Using NullAudio"); 172 return new HipNullAudio(); 173 } 174 } 175 } 176 177 178 179 protected bool hasProAudio; 180 protected bool hasLowLatencyAudio; 181 protected int optimalBufferSize; 182 protected int optimalSampleRate; 183 private bool is3D; 184 private uint activeSources; 185 186 IHipAudioPlayer audioInterface; 187 188 //Debug vars 189 private bool _hasInitializedAudio = false; 190 public bool hasInitializedAudio() => _hasInitializedAudio; 191 } 192 193 194 private __gshared HipAudioImpl player; 195 void PreInitializeHipAudio() 196 { 197 player = new HipAudioImpl(); 198 setIHipAudioPlayer(player); 199 } 200 201 pragma(inline, true) 202 HipAudioImpl HipAudio(){return player;} 203 204 export extern(C) IHipAudioPlayer HipAudioPlayerAPI() 205 { 206 return player; 207 }